#pragma once
#include "LocalProxy.h"
#include "Console.h"
#include "LPDef.h"
#include "LPNet.h"
#include "LPHTTPHeader.h"
#include "LPHTTPRequest.h"
#include "FormatPrint.h"
#include "LPHTTPResponse.h"
namespace LocalProxy{
#pragma warning(push)
#pragma warning(disable:4996 )
	class CProxyThread
	{
		enum ProxyProcess
		{
			PP_RECV_LOCAL_REQUEST,
			/*PP_CONNECT_TO_REMOTE_SERVER,*/
			PP_RECV_POST_DATA_SEND_TO_REMOTE,
			PP_MAKE_REMOTE_REQUEST,
			PP_SEND_REQUEST,
			PP_RECV_RESPONSE_HEADER,
			PP_RECV_REMOTE_RESPONSE_TO_LOCAL,
		};
	public:		
		static void NewWorkThread(SOCKET sock,sockaddr_in sockaddr)
		{
			new CProxyThread(sock,sockaddr);			
		}
	protected:
		CProxyThread(SOCKET sock,sockaddr_in sockaddr)
			:m_sockLocal(sock)
			,m_addrRemote(sockaddr)
			,m_pSendRequestHeaderData(NULL)
			//,m_dwDataLast(0)
			,m_dwSendHeaderDataLast(0)
			,m_dwSendPostDataLast(0)
			,m_dwSendContentDataLast(0)
			,m_ProxyProcess(PP_RECV_LOCAL_REQUEST)
			,m_cntRecvResponseHeader(0)
			,m_cntRecvRequestHeader(0)
			,m_dwRecvedContentFromRemote(0)
		{
			_beginthread(_WorkThread,0,(void*)this);
		}

		BOOL RecvRequest()
		{
			CTempBuffer<CHAR> recvBuf;
			
			const size_t bufLen=4096;
			LPCH pRecvBuf=recvBuf.Allocate(bufLen);
			
			//FormatPrint::CFormatPrint<console> dbgPrint;
			while(1)
			{
				int rcvLen=recv(m_sockLocal,pRecvBuf,bufLen,0);
				if(rcvLen==0)
				{
					break;
				}
				//dbgPrint.Print(pRecvBuf,rcvLen);
				m_RequestRecv.Read(pRecvBuf,rcvLen);
				if(m_RequestRecv.End())
				{ 					
					break;
				}
			}
			if(m_RequestRecv.End())
				return TRUE;
			return FALSE;
		}

		BOOL RecvRequestHeader()
		{
			m_cntRecvRequestHeader++;
			CAtlIsapiBuffer<> bufPrint;
			FormatPrint::CFormatPrint<console> dbgPrint;
			int iHeaderLength=0;
			char chRecv;
			while(1)
			{
				int rcvLen=m_sockLocal.Recv(&chRecv,1);
				if(rcvLen<=0)
				{
					m_sockLocal.Close();
					break;
				}
				iHeaderLength++;
				if(iHeaderLength>MAX_HEADER_LENGTH)
				{
					ATLASSERT(0);
					m_sockLocal.Close();
					break;
				}
				bufPrint.Append(&chRecv,1);
				//dbgPrint.Print(pRecvBuf,rcvLen);
				if(m_RequestRecv.ReadChar(chRecv)<=0)
				{
					return FALSE;
				}
				if(m_RequestRecv.HeaderEnd())
				{					
					dbgPrint.Print(bufPrint,bufPrint.GetLength());
					return TRUE;
				}
			}
			if(m_RequestRecv.HeaderEnd())
			{
				dbgPrint.Print(bufPrint,bufPrint.GetLength());
				return TRUE;
			}
			return FALSE;
		}

// 		BOOL PrepareRequest()
// 		{
// 			m_RequestSend.CopyFrom(m_RequestRecv);			
// 			//m_RequestSend.SetConnection(CT_Close);
// 			CAtlIsapiBuffer<> &buf=m_RequestSend.GetStream();
// 			m_dwSendHeaderDataLast=buf.GetLength();
// 			m_pSendRequestHeaderData=buf;
// 			//m_RequestSend.SetURI(m_RequestSend.)
// 			return TRUE;
// 		}

// 		BOOL SendRequest()
// 		{
// 			DWORD dwSended=0;
// 			if(!m_sockRemote.IsValid())
// 			{
// 				if(!m_sockRemote.Create(AF_INET,SOCK_STREAM,IPPROTO_TCP))
// 				{
// 					ATLASSERT(0);
// 					return FALSE;
// 				}
// 				struct hostent *pHostent=gethostbyname(CStringA(m_RequestSend.GetHost()));
// 				if(!pHostent)
// 				{
// 					ATLASSERT(0);
// 					return FALSE;
// 				}
// 
// 				sockaddr_in addr={0};
// 				addr.sin_addr.s_addr=*(u_long*)pHostent->h_addr_list[0];
// 				addr.sin_family=AF_INET;
// 				addr.sin_port=htons(m_RequestSend.GetPort());
// 
// 				if(SOCKET_ERROR==connect(m_sockRemote,(const struct sockaddr*)&addr,sizeof(struct sockaddr)))
// 				{
// 					//ATLASSERT(0);
// 					return FALSE;
// 				}
// 			}
// 			
// 
// 			while(m_dwDataLast)
// 			{
// 				dwSended+=send(m_sockRemote,m_pSendRequestHeaderData+dwSended,m_dwDataLast,0);
// 				m_dwDataLast-=dwSended;
// 			}
// 			return TRUE;
// 		}

// 		BOOL GetResponse()
// 		{
// 			CTempBuffer<CHAR> recvBuf;
// 			const size_t bufLen=1024;
// 			LPCH pRecvBuf=recvBuf.Allocate(bufLen);			
// 			while(1)
// 			{
// 				int rcvLen=m_sockRemote.Recv(pRecvBuf,bufLen);
// 				if(rcvLen<=0)
// 				{
// 					break;
// 				}
// 				m_ResponseRecv.Read(pRecvBuf,bufLen);
// 			}
// 			return TRUE;
// 		}

// 		BOOL SendResponse()
// 		{
// 			if(m_bufResponse.GetLength())
// 			{
// 				DWORD dwDataLast=m_bufResponse.GetLength();
// 				LPCSTR pSendData=m_bufResponse;
// 				DWORD dwSended=0;
// 				while(dwDataLast)
// 				{
// 					dwSended+=send(m_sockLocal,pSendData+dwSended,dwDataLast,0);
// 					dwDataLast-=dwSended;
// 				}
// 			}
// 			else
// 			{
// 				static const char m_badResponse[]=
// 					"HTTP/1.1 500 Internal Server Error\r\n\r\n";
// 				send(m_sockLocal,m_badResponse,sizeof(m_badResponse)-1,0);
// 			}
// 
// 
// 			return TRUE;
// 		}

// 		BOOL ConnectLocalAndRemote()
// 		{
// 			CTempBuffer<CHAR> recvBuf;			
// 			const size_t bufLen=1024;
// 			size_t needRecv=bufLen;
// 			size_t nReadedContent=0;
// 			size_t nContentLength=0;
// 			LPCH pRecvBuf=recvBuf.Allocate(bufLen);			
// 			int iPrinted=0;
// 			FormatPrint::CFormatPrint<console> dbgPrint;
// 			while(needRecv)
// 			{
// 				const int rcvLen=m_sockRemote.Recv(pRecvBuf,(int)needRecv);
// 				if(rcvLen<=0)
// 				{
// 					break;
// 				}
// 				//dbgPrint.Print(pRecvBuf,bufLen);
// 				if(!m_ResponseRecv.End())
// 				{
// 					const int resLen=m_ResponseRecv.Read(pRecvBuf,(int)needRecv);
// 					if(m_ResponseRecv.End())
// 					{
// 						nContentLength=m_ResponseRecv.GetContentLength();
// 						if(nContentLength==0)
// 						{
// 							needRecv=bufLen;							
// 						}
// 						else
// 						{
// 							nReadedContent=rcvLen-resLen;
// 							needRecv=nContentLength-nReadedContent;
// 							needRecv=needRecv>bufLen?bufLen:needRecv;
// 						}						
// 					}
// 					dbgPrint.Print(pRecvBuf,resLen);
// 				}
// 				else
// 				{
// 					nContentLength=m_ResponseRecv.GetContentLength();
// 					if(nContentLength==0)
// 					{
// 						needRecv=bufLen;						
// 					}
// 					else
// 					{
// 						nReadedContent+=rcvLen;
// 						needRecv=nContentLength-nReadedContent;
// 						needRecv=needRecv>bufLen?bufLen:needRecv;
// 					}					
// 				}
// 				
// 				int sndLen=m_sockLocal.Send(pRecvBuf,rcvLen);
// 				if(sndLen<=0)
// 				{
// 					break;
// 				}
// 			}
// 			return TRUE;
// 		}

		BOOL SendError()
		{
			static const char m_badResponse[]=
				"HTTP/1.1 500 Internal Server Error\r\n\r\n";
			send(m_sockLocal,m_badResponse,sizeof(m_badResponse)-1,0);
			return TRUE;
		}

		BOOL ConnectToRemoteServer()
		{
			//ATLASSERT(m_sockRemote.IsValid());
			if(m_sockRemote.IsValid())
			{
				return TRUE;
				//m_sockRemote.Close();
			}
			if(!m_sockRemote.Create(AF_INET,SOCK_STREAM,IPPROTO_TCP))
			{
				ATLASSERT(0);
				return FALSE;
			}
			struct hostent *pHostent=gethostbyname(CStringA(m_RequestRecv.GetHost()));
			if(!pHostent)
			{				
				return FALSE;
			}

			sockaddr_in addr={0};
			addr.sin_addr.s_addr=*(u_long*)pHostent->h_addr_list[0];
			addr.sin_family=AF_INET;
			addr.sin_port=htons(m_RequestRecv.GetPort());

			if(SOCKET_ERROR==connect(m_sockRemote,(const struct sockaddr*)&addr,sizeof(struct sockaddr)))
			{
				return FALSE;
			}
			
			return TRUE;
		}

		BOOL PrepareRequestHeader()
		{
			m_RequestSend.CopyFrom(m_RequestRecv);
			ConnectionType ct=m_RequestRecv.GetProxyConnection();
			if(ct==CT_Unknow)
			{
				ct=m_RequestRecv.GetConnection();
			}
			if(ct==CT_Unknow)
			{
				ct=CT_Close;
			}
			m_RequestSend.SetConnection(ct);
			CAtlIsapiBuffer<> &buf=m_RequestSend.GetStream();
			m_dwSendHeaderDataLast=buf.GetLength();
			m_pSendRequestHeaderData=buf;
			//m_RequestSend.SetURI(m_RequestSend.)
			return TRUE;			
		}

		BOOL SendRequestHeader()
		{
			int nSended=0;
			while(m_dwSendHeaderDataLast)
			{
				nSended+=m_sockRemote.Send(m_pSendRequestHeaderData+nSended,m_dwSendHeaderDataLast,0);
				if(nSended<=0)
				{
					ATLASSERT(0);
					return FALSE;
				}
				m_dwSendHeaderDataLast-=(DWORD)nSended;
			}
			return TRUE;
		}

		BOOL RecvPostDataSendToRemote()
		{
			ATLASSERT(m_sockRemote.IsValid());
			CTempBuffer<CHAR> buf;
			CHAR *pBuf=buf.Allocate(1024);
			const int rcvLen=m_sockLocal.Recv(pBuf,1024);
			const int sndLen=m_sockRemote.Send(pBuf,rcvLen);
			ATLASSERT(rcvLen==sndLen);
			if(sndLen<=0)
			{
				return FALSE;
			}
			if(rcvLen<=0)
			{
				return FALSE;
			}
			if(rcvLen!=sndLen)
			{
				return FALSE;
			}

			m_dwSendPostDataLast-=rcvLen;
			return TRUE;
		}

// 		BOOL RecvRemoteResponseToLocal_old()
// 		{
// 			CTempBuffer<CHAR> recvBuf;			
// 			const size_t bufLen=1024;
// 			size_t needRecv=bufLen;
// 			size_t nReadedContent=0;
// 			size_t nContentLength=0;
// 			LPCH pRecvBuf=recvBuf.Allocate(bufLen);			
// 			int iPrinted=0;
// 			FormatPrint::CFormatPrint<console> dbgPrint;
// 		
// 			const int rcvLen=m_sockRemote.Recv(pRecvBuf,(int)bufLen);
// 			if(rcvLen<=0)
// 			{
// 				return FALSE;
// 			}
// 			//dbgPrint.Print(pRecvBuf,bufLen);
// 			if(!m_ResponseRecv.End())
// 			{
// 				const int resLen=m_ResponseRecv.Read(pRecvBuf,(int)needRecv);
// 				if(m_ResponseRecv.End())
// 				{
// 						dbgPrint.Print(pRecvBuf,resLen);			
// 				}				
// 			}			
// 
// 			int sndLen=m_sockLocal.Send(pRecvBuf,rcvLen);
// 			if(sndLen<=0)
// 			{
// 				return FALSE;
// 			}
// 			
// 			return TRUE;
// 		}

		BOOL RecvRemoteResponseToLocal()
		{
			CTempBuffer<CHAR> recvBuf;			
			const size_t bufLen=m_dwSendContentDataLast>1024?1024:m_dwSendContentDataLast;
			size_t needRecv=bufLen;
			size_t nReadedContent=0;
			size_t nContentLength=0;
			LPCH pRecvBuf=recvBuf.Allocate(bufLen);			
			int iPrinted=0;
			FormatPrint::CFormatPrint<console> dbgPrint;

			const int rcvLen=m_sockRemote.Recv(pRecvBuf,(int)bufLen);
			if(rcvLen<=0)
			{
				return FALSE;
			}
			const int sndLen=m_sockLocal.Send(pRecvBuf,rcvLen);
			if(sndLen<=0)
			{
				return FALSE;
			}
			ATLASSERT(sndLen==rcvLen);
			if(sndLen!=rcvLen)
			{
				return FALSE;
			}
			m_dwRecvedContentFromRemote+=rcvLen;
			m_dwSendContentDataLast-=sndLen;
			return TRUE;
		}

		BOOL RecvResponseHeader()
		{
			m_cntRecvResponseHeader++;
			CAtlIsapiBuffer<> bufPrint;
			FormatPrint::CFormatPrint<console> dbgPrint;
			int iHeaderLength=0;
			char chRecv;
			while(1)
			{
				int rcvLen=m_sockRemote.Recv(&chRecv,1);
				if(rcvLen<=0)
				{
					m_sockRemote.Close();
					break;
				}
				iHeaderLength++;
				if(iHeaderLength>MAX_HEADER_LENGTH)
				{
					ATLASSERT(0);
					m_sockLocal.Close();
					break;
				}
				bufPrint.Append(&chRecv,1);
				//dbgPrint.Print(pRecvBuf,rcvLen);
				if(m_ResponseRecv.ReadChar(chRecv)<=0)
				{
					return FALSE;
				}
				m_sockLocal.Send(&chRecv,1);
				if(m_ResponseRecv.HeaderEnd())
				{					
					dbgPrint.Print(bufPrint,bufPrint.GetLength());
					if(!(m_dwSendContentDataLast=m_ResponseRecv.GetContentLength()))
					{
						m_dwSendContentDataLast=-1;
					}
					return TRUE;
				}
			}		
			return FALSE;
		}

		void Run()
		{
			fd_set fs_read;
			fd_set fs_send;
			fd_set fs_exp;
			int nfd;
			timeval tv={0};
// 			FD_ZERO(&fs_send);
// 			FD_ZERO(&fs_exp);
// 			FD_SET(m_sockLocal,&fs_read);
			BOOL bContinue=TRUE;
			
			while(bContinue)
			{				
				tv.tv_sec=LONG_MAX;
				tv.tv_usec=LONG_MAX;
				FD_ZERO(&fs_read);
				FD_ZERO(&fs_send);
				FD_ZERO(&fs_exp);
				FD_SET(m_sockLocal,&fs_read);
				nfd=1;
				if(m_sockRemote.IsValid())
				{
					FD_SET(m_sockRemote,&fs_read);					
					nfd++;
				}				
				nfd=select(nfd,&fs_read,&fs_send,&fs_exp,&tv);
				if(nfd<=0)
				{
					//ATLASSERT(0);
					delete this;
					return ;
				}
				if(FD_ISSET(m_sockRemote,&fs_read))
				{
					switch(m_ProxyProcess)
					{
					case PP_RECV_RESPONSE_HEADER:
						{
							console::print("PP_RECV_RESPONSE_HEADER:RecvResponseHeader\n");
							if(!RecvResponseHeader())
							{
								bContinue=FALSE;
								break;
							}
							else
							{
								m_ProxyProcess=PP_RECV_REMOTE_RESPONSE_TO_LOCAL;
							}
						}
						break;
					case PP_RECV_REMOTE_RESPONSE_TO_LOCAL:
						{
							console::print("PP_RECV_REMOTE_RESPONSE_TO_LOCAL:RecvRemoteResponseToLocal\n");

							if(!RecvRemoteResponseToLocal())
							{
								bContinue=FALSE;
								break;
							}
							if(m_dwSendContentDataLast==0)
							{
								console::print("PP_RECV_REMOTE_RESPONSE_TO_LOCAL\n");

								m_ProxyProcess=PP_RECV_LOCAL_REQUEST;
								m_RequestRecv.Reset();
								m_RequestSend.Reset();
								m_ResponseRecv.Reset();
								m_dwSendHeaderDataLast=0;
								m_dwSendPostDataLast=0;
								m_dwSendContentDataLast=0;
							}
						}
						break;
					
					default:
						{
							CAtlIsapiBuffer<> dbg;
							char buf[1024];
							while(1)
							{
								int n=m_sockRemote.Recv(buf,1024);
								if(n<=0)
									break;
								dbg.Append(buf,n);
							}
							ATLASSERT(0);
							bContinue=FALSE;
							break;
						}
						break;
					}
				}
				if(FD_ISSET(m_sockLocal,&fs_read))
				{
					switch(m_ProxyProcess)
					{
					case PP_RECV_LOCAL_REQUEST:
						{
							console::print("PP_RECV_LOCAL_REQUEST:RecvRequestHeader\n");

							if(!RecvRequestHeader())
							{
								bContinue=FALSE;
								break;
							}
							console::print("PP_RECV_LOCAL_REQUEST:ConnectToRemoteServer\n");

							if(!ConnectToRemoteServer())
							{
								bContinue=FALSE;
								break;
							}
							console::print("PP_RECV_LOCAL_REQUEST:PrepareRequestHeader\n");

							if(!PrepareRequestHeader())
							{
								bContinue=FALSE;
								break;
							}
							console::print("PP_RECV_LOCAL_REQUEST:SendRequestHeader\n");

							if(!SendRequestHeader())
							{
								bContinue=FALSE;
								break;
							}

							
							if(m_RequestRecv.GetMethod()==RM_POST)
							{
								console::print("PP_RECV_LOCAL_REQUEST:m_RequestRecv.GetMethod()==RM_POST\n");
								if(m_dwSendPostDataLast=m_RequestRecv.GetContentLength())
								{
									m_ProxyProcess=PP_RECV_POST_DATA_SEND_TO_REMOTE;
								}
								else
								{
									ATLASSERT(0);
									bContinue=FALSE;
									break;
								}
							}
							else
							{
								console::print("PP_RECV_LOCAL_REQUEST:m_RequestRecv.GetMethod()!=RM_POST\n");

								m_ProxyProcess=PP_RECV_RESPONSE_HEADER;
							}
						}
						break;
					case PP_RECV_POST_DATA_SEND_TO_REMOTE:
						{
							console::print("PP_RECV_POST_DATA_SEND_TO_REMOTE:RecvPostDataSendToRemote\n");

							if(!RecvPostDataSendToRemote())
							{
								bContinue=FALSE;
								break;
							}
							if(m_dwSendPostDataLast==0)
							{
								m_ProxyProcess=PP_RECV_RESPONSE_HEADER;
							}
						}
						break;
					case PP_RECV_REMOTE_RESPONSE_TO_LOCAL:
						{
							console::print("PP_RECV_REMOTE_RESPONSE_TO_LOCAL\n");

							m_ProxyProcess=PP_RECV_LOCAL_REQUEST;
							m_RequestRecv.Reset();
							m_RequestSend.Reset();
							m_ResponseRecv.Reset();
							m_dwSendHeaderDataLast=0;
							m_dwSendPostDataLast=0;
							m_dwSendContentDataLast=0;
						}
						break;
					default:
						{
							ATLASSERT(0);
							bContinue=FALSE;
						}
						break;
					}
				}				
// 				RecvRequest()&&PrepareRequest()&&SendRequest();//&&GetResponse();
// 				
// 				if(m_sockRemote.IsValid())
// 				{
// 					ConnectLocalAndRemote();
// 				}
// 				else
// 				{
// 					SendError();
// 					delete this;
// 					return ;
// 				}
// 				if(m_ResponseRecv.GetConnection()!=CT_Keep_Alive)
// 				{
// 					delete this;
// 					return ;
// 				}
// 				m_ResponseRecv.Reset();
// 				m_RequestRecv.Reset();
// 				m_RequestSend.Reset();
			}
			delete this;
		}

		static void _WorkThread(void *p)
		{
			((CProxyThread *)(p))->Run();			
		}
		CHTTPRequestParser m_RequestRecv;
		CHTTPRequestMaker m_RequestSend;
		CHTTPResponseParser m_ResponseRecv;
		CSocket m_sockLocal;
		CSocket m_sockRemote;
		CAtlIsapiBuffer<> m_bufResponse;
		sockaddr_in m_addrRemote;
		LPCSTR m_pSendRequestHeaderData;
		DWORD m_dwSendHeaderDataLast;
		DWORD m_dwSendPostDataLast;
		DWORD m_dwSendContentDataLast;

		DWORD m_dwRecvedContentFromRemote;
		DWORD m_cntRecvResponseHeader,m_cntRecvRequestHeader;
		ProxyProcess m_ProxyProcess;
	};

#pragma warning(pop)
}
using namespace LocalProxy;